package com.example.httpsocket.tomcat;

import com.example.httpsocket.entity.MyRequest;
import com.example.httpsocket.entity.MyResponse;
import com.example.httpsocket.servlet.parentServlet.MyHttpServlet;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Huangyt
 * @version 1.0
 * @date 2020/8/27 12:20
 */
@Slf4j
public class MyTomcat {
    private static int PORT = 8080;
    private static HashMap<String, String> servletMapping = new HashMap<>(); //维护<servlet>标签信息
    private static HashMap<String, String> urlMapping = new HashMap<>(); //维护<servlet-mapping>标签信息
    private static HashMap<String, MyHttpServlet> servletMap = new HashMap<>(); //存储真正的Servlet类

    private MyTomcat() {
        log.info("MyTomcat被初始化");
    }

    //静态内部类，只有调用相关代码时才会初始化，且只初始化一次
    private static class LazyHolder {
        static final MyTomcat mytomcat = new MyTomcat();
    }

    /*
        初始化Tomcat中的Servlet容器信息
     */
    private void init() throws DocumentException {
        String WEBXMLPath = System.getProperty("user.dir") + "\\src\\main\\webapp\\WEB-INF\\web.xml"; //获取web.xml绝对路径
        SAXReader saxReader = new SAXReader(); //获取DOM4J读取器
        Document document = saxReader.read(WEBXMLPath); //扫描web.xml
        Element rootElement = document.getRootElement(); //获取除<web-app外>所有根标签
        List<Element> elements = rootElement.elements(); //获取所有根标签中的集合标签

        elements.forEach((element) -> { //遍历根标签下的子标签
            if ("servlet".equalsIgnoreCase(element.getName())) {
                System.out.println("element:" + element);
                Element servletName = element.element("servlet-name");
                Element servletClass = element.element("servlet-class");
                log.info("servletName:" + servletName.getText());
                log.info("servletClass:" + servletClass.getText());
                servletMapping.put(servletName.getText(), servletClass.getText()); //维护<servlet>标签信息
            } else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
                System.out.println("element:" + element);
                Element servletName = element.element("servlet-name");
                Element urlpattern = element.element("url-pattern");
                log.info("servletName:" + servletName.getText());
                log.info("servletClass:" + urlpattern.getText());
                urlMapping.put(urlpattern.getText(), servletName.getText()); //维护<servlet-mapping>标签信息,注意这里url作为key，servletName作为value
            }
        });
    }

    /*
        初始化Tomcat中的Servlet容器信息
     */
    public void start() {
        ServerSocket serverSocket = null;
        try {
            init(); //初始化Tom
            InetAddress inetAddress = inetAddress = InetAddress.getLocalHost();
            serverSocket = new ServerSocket(PORT, 10, inetAddress);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            while (!serverSocket.isClosed()) {
                log.info("等待http请求....");
                Socket server = serverSocket.accept();
                log.info("接收http请求....");
                ExecutorService tomcatPool = Executors.newFixedThreadPool(10); //Tomcat线程池
                tomcatPool.submit(new HttpAcceptThread(server));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /*
        Tomcat处理请求线程类
     */
    public class HttpAcceptThread implements Runnable {
        private Socket socket;

        public HttpAcceptThread(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            BufferedReader reader = null;
            OutputStream outputStream = null;
            String msg = null;
            String header = null;
            try {
                log.info("http处理进程开始!");
                MyRequest myRequest = new MyRequest(socket.getInputStream()); //解析http请求，MyRequest包装请求报文
                MyResponse myResponse = new MyResponse(socket.getOutputStream()); //MyResponse包装响应报文
                System.out.println("request请求封装：" + myRequest);

                MyHttpServlet handler = getHandlerByRequest(myRequest, myResponse); //根据请求类获取Servlet处理类
                handler.service(myRequest, myResponse); //调用Servlet处理类方法

                log.info("http处理进程结束!");
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.getInputStream().close();
                    outputStream.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /*
            根据请求类获取Servlet处理类
         */
        private MyHttpServlet getHandlerByRequest(MyRequest request, MyResponse response) {
            MyHttpServlet handler = null;

            String uri = request.getUri();
            String servletName = urlMapping.get(uri); //根据uri获取servlet标签名称
            if (!servletMap.containsKey(servletName)) { //懒加载：调用Servlet时才初始化
                String servlet = servletMapping.get(servletName); //根据servlet标签名称获取servlet全类名路径
                try {
                    handler = (MyHttpServlet) Class.forName(servlet).newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                    response.write(404, "请求uri资源不存在", "Content-Type:application/json", null);
                }
                servletMap.put(servletName, handler);
            } else { //Servlet初始化后直接调用
                handler = servletMap.get(servletName);
                if (handler == null) {
                    response.write(500, "servlet初始化异常", "Content-Type:application/json", null);
                }
            }
            return handler;
        }
    }

    /*
        单例获取Tomcat实例
     */
    public static MyTomcat getTomcat() {
        return LazyHolder.mytomcat;
    }
}
